using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using OpenSource.SpreadsheetFactory;
using OpenSource.SpreadsheetFactory.Internal.Xls;
//using OpenSource.SpreadsheetFactory.Internal.Xlsx;
using OpenSource.SpreadsheetFactory.Internal.Csv;
using OpenSource.SpreadsheetFactory.Internal.Html;

namespace OpenSource.SpreadsheetFactory.Internal
{
    internal abstract class SpreadsheetGenerator<T>
    {
        private List<Column<T>> columns { get; set; }
        protected IEnumerable<T> Source { get; set; }
        protected string Title { get; set; }

        protected SpreadsheetGenerator(IEnumerable<T> source)
        {
            this.Source = source;
            this.columns = new List<Column<T>>();
            this.FetchHeaderOptions();
            this.EnumerateColumns();
            this.SortColumns();
        }

        internal static SpreadsheetGenerator<T> CreateSpreadsheetGenerator(IEnumerable<T> source, SpreadsheetType spreadsheetType)
        {
            switch (spreadsheetType)
            {
                case SpreadsheetType.Xls: return new XlsGenerator<T>(source);
                case SpreadsheetType.Xlsx:
                    throw new NotSupportedException(); //return new XlsxGenerator<T>(source);
                case SpreadsheetType.Csv: return new CsvGenerator<T>(source);
                case SpreadsheetType.Html: return new HtmlGenerator<T>(source);
                default: throw new ArgumentOutOfRangeException("spreadsheetType");
            }
        }

        private void EnumerateColumns()
        {
            MemberInfo[] members = typeof(T).GetMembers(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
            foreach (MemberInfo memberInfo in members)
            {
                Column<T> column = SpreadsheetGenerator<T>.GetColumn(memberInfo);
                if (column != null)
                {
                    this.columns.Add(column);
                }
            }
        }

        private void SortColumns()
        {
            columns = columns.OrderBy(col => col.Ordinal).ToList();
        }

        private void FetchHeaderOptions()
        {
            SpreadsheetFactoryOptionsAttribute attribute = Attribute.GetCustomAttribute(typeof(T), typeof(SpreadsheetFactoryOptionsAttribute)) as SpreadsheetFactoryOptionsAttribute;
            if (attribute == null)
            {
                this.Title = typeof(T).Name;
            }
            else
            {
                this.Title = attribute.Title;
            }
        }

        internal abstract byte[] GenerateSpreadsheet();

        private static Column<T> GetColumn(MemberInfo memberInfo)
        {
            return Column<T>.CreateColumn(memberInfo);
        }

        protected IEnumerable<Column<T>> Columns
        {
            get
            {
                return this.columns;
            }
        }
    }
}

